package com.ar4j.handler;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import org.antlr.stringtemplate.StringTemplate;
import org.apache.commons.lang.StringUtils;

import com.ar4j.ActiveRecordContext;
import com.ar4j.ActiveRecordMetadata;
import com.ar4j.util.CollectionUtils;

/**
 * Various common handler utilities
 */
public final class HandlerUtils {
  
  private HandlerUtils() {
    // UTILITY CLASS - NO CONSTRUCTOR FOR YOU!
  }
  
  /**
   * Goes through the given parameters, makes sure that all parameter names are properties in the provided
   * active record metadata. Records field names and collection field flags in the given maps.
   * 
   * @param parameters
   * @param metadata
   * @param fields
   * @param collectionFields
   */
  public static void scanParametersForFields(Map<String, Object> parameters, ActiveRecordMetadata metadata, 
      Collection<String> fields, Collection<String> collectionFields) {
    
    for(Map.Entry<String, Object> entry : parameters.entrySet()) {
      if(!metadata.getPropertyNames().contains(entry.getKey())) {
        throw new IllegalArgumentException("Unknown field in parameter map: " + entry.getKey() + " for base class: " + metadata.getBaseClass());
      }
      
      fields.add(entry.getKey());
      if(entry.getValue() != null && (entry.getValue().getClass().isArray() || Collection.class.isAssignableFrom(entry.getValue().getClass()))) {
        collectionFields.add(entry.getKey());
      }
    }
  }
  
  /**
   * Creates a real query string using the given query template, metadata and context objects.
   * 
   * Some default template attributes are as follows:
   * table - the table name
   * schema - the schema name, if any
   * qualifiedTable - a schema qualifier table name, if any, in the form of "schemaName.tableName"
   * idName - the name of the identifier column
   * 
   * Other attribute values can be provided using the given map.
   */
  public static String renderQuery(StringTemplate queryTemplate, ActiveRecordMetadata metadata, ActiveRecordContext context, Map<String, Object> attributeValues) {
    
    // figure out qualified table name
    String qualifiedTableName = metadata.getTableName();
    if(StringUtils.isNotBlank(context.getSchemaName())) {
      qualifiedTableName = context.getSchemaName() + "." + qualifiedTableName;
    }
    
    // copy the template and set default vars
    StringTemplate query = queryTemplate.getInstanceOf();
    query.setAttribute("table", metadata.getTableName());
    query.setAttribute("schema", context.getSchemaName());
    query.setAttribute("qualifiedTable", qualifiedTableName);
    query.setAttribute("idName", metadata.getPropertyColumnNameMap().get(metadata.getIdentifierPropertyName()));
    
    // set other attributes as needed
    for(Map.Entry<String, Object> entry : attributeValues.entrySet()) {
      query.setAttribute(entry.getKey(), entry.getValue());
    }
    
    // render
    return query.toString();
  }
  
  /**
   * Similar to renderQuery() but with no extra attributes
   */
  public static String renderQuery(StringTemplate queryTemplate, ActiveRecordMetadata metadata, ActiveRecordContext context) {
    Map<String, Object> empty = Collections.emptyMap();
    return renderQuery(queryTemplate, metadata, context, empty);
  }
  
  /**
   * Similar to renderQuery() but with an assumed attribute with the name of "where"
   */
  public static String renderQueryWithWhere(StringTemplate queryTemplate, ActiveRecordMetadata metadata, ActiveRecordContext context, String whereClause) {
    Map<String, Object> values = CollectionUtils.getSingleValueMap("where", (Object) whereClause);
    return renderQuery(queryTemplate, metadata, context, values);
  }
  
  /**
   * Similar to renderQuery() but with an assumed attribute with the name of "suffix"
   */
  public static String renderQueryWithQuerySuffix(StringTemplate queryTemplate, ActiveRecordMetadata metadata, ActiveRecordContext context, String querySuffix) {
    Map<String, Object> values = CollectionUtils.getSingleValueMap("suffix", (Object) querySuffix);
    return renderQuery(queryTemplate, metadata, context, values);
  }
  
}
